
/*!
 * -------------------------------------------------------------
 * 对easyui的封装, 解决tabs的panel模块化的问题
 * 尽量避免全局变量污染, 避免全局selector的引用
 * -------------------------------------------------------------
 * @author 赵卉华
 * date: 2016-01-22
 * V2.2 2017-07-31
 * V2.3 2017-09-14
 * -------------------------------------------------------------
 */
(function($){
	var xui = window.xui = { serial:0, original:{} };

	var VARS = /(['"]?)\b(_*\w+)\b(["']?\s*:\s*)([_\$a-z][\$\.\w]+)/gim;
	var BASE = /^(true|false|function|this|window|null|undefined|[\.\d]+)$/;
	var DELAY = "x-delay";
	var ARRAY_SPLIT = /\s*,\s*|\s*\/\s*|\s*\|\s*/g;

	if (!$.fn.panel.methods.setIconCls) { // 给panel增加setIconCls方法
		$.fn.panel.methods.setIconCls = function(jq, iconCls) {
			return jq.each(function () {
				var me = $(this);
				me.panel("options").iconCls = iconCls;
				me.panel("header").find("div.panel-icon").attr("class", "panel-icon " + iconCls);
			});
		};
	}
	if (!$.fn.linkbutton.methods.setText) { // 给linkbutton增加setText方法
		$.fn.linkbutton.methods.setText = function(jq, text) {
			return jq.each(function () {
				var me = $(this);
				me.linkbutton("options").text = text;
				me.find(".l-btn-text").html(text);
			});
		};
	}
	if (!$.fn.textbox.methods.focus) { // 给textbox增加focus方法
		$.fn.textbox.methods.focus = function(jq) {
			return jq.each(function () {
				$(this).next(".textbox").find(".textbox-text").focus();
			});
		};
	}
	if (!$.fn.combotree.methods.getData) { // 给combotree增加getData方法
		$.fn.combotree.methods.getData = function(jq) {
			var opts = $.data(jq.get(0), "combotree").options;
			return opts.data;
		};
	}

	// 某些复杂的prompt放在options中配置会报错, 改为placeholder
	// 如: data-options="prompt:'格式1--&gt; MALE:男 | FEMALE:女&#13;&#10;格式2--&gt; 男|女'"
	var textboxParseOptions = $.fn.textbox.parseOptions;
	$.fn.textbox.parseOptions = function(elem) {
		var opts = textboxParseOptions(elem);
		if (!opts.prompt) {
			var placeholder = $(elem).attr("placeholder");
			if (placeholder) {
				opts.prompt = placeholder;
			}
		}
		return opts;
	};

	var toOptions = function(item) {
		if (typeof(item) == "string") {
			// 简写转换为完整格式, 'Gender' -- {'Gender':'Gender'}
			var temp = {};
			temp[item] = item;
			return temp;
		} else {
			return item;
		}
	};
	// options = { type:string, first:boolean, xref:json }
	var initElement = function(options) {
		var type = options.type;
		var me = $(this);

		var copyOptions = {};
		var copyKey = me.attr("data-copy-options");
		if (copyKey) {
			var keys = copyKey.split(/[,\s]/g);
			for (var i = 0; i < keys.length; i++) {
				$.extend(copyOptions, $.zhh.field(xui[options.xref.serial], keys[i]));
			}
		}

		var type = options.type;
		if (me.data("x-call")) {
			$.extend(true, me.xcall("options"), { xref:options.xref }, copyOptions);
			return true; // 已经初始化
		}
		me.data("x-call", type);
		if (me.data(type)) {
			$.extend(true, me.xcall("options"), { xref:options.xref }, copyOptions);
			return true; // 已经初始化
		}

		var defined = DEF.plugins[type];
		if (defined) {
			defined.call(this, $.extend(options, { copyOptions:copyOptions }));
		} else {
			me[type]($.extend({ xref:options.xref }, copyOptions));
		}
	};
	var initTabs = function() {
		return this.each(function() {
			$(this).tabs({
				// 新TAB页加载成功时触发
				onLoad:function(pnl) {
					pnl.addClass("x-space").data("x-call", "panel");
					var options = pnl.panel("options");
					var space = options.initial.space;
					// 加载初始参数: <div class="x-initial" data-options="{fixedParams:{sceneType:'mamager'}}"></div>
					pnl.find(".x-initial").each(function() {
						var xopts = $.parser.parseOptions(this);
						$.extend(options.initial, xopts);
					});
					pnl.each(function() {
						$.zhh.events.trigger(this, space, $(this), options.initial, options.extras);
					});
					pnl.find("link").removeAttr("disabled");
					pnl.find("style").removeAttr("disabled");
				},
				// 选中TAB页时触发
				onSelect:function(title, index) {
					var me = $(this);
					var pnl = me.tabs("getTab", index);
					var popts = pnl.panel("options");
					var initial = popts.initial || {};
					// 选中左侧菜单
					var menu = initial.menu;
					if (menu) {
						menu.trigger("selected", [menu]);
					}
					if (initial.homepage) {
						if (location.hash) { location.hash = ""; }
					} else {
						location.hash = initial.hash || "";
					}
					// 首次打开TAB页触发的onSelect时, 是没有子元素的
					// 从其他TAB页切换回现有的TAB页时才有子元素
					if (pnl.children().length) {
						pnl.find("link").removeAttr("disabled");
						pnl.find("style").removeAttr("disabled");
						if (popts.refreshChildrenOnSelect) {
							pnl.xreload(true); // 刷新子元素
						}
					}
				},
				// 取消选择TAB页时触发
				onUnselect:function(title, index) {
					var me = $(this);
					var pnl = me.tabs("getTab", index);
					pnl.find("link").attr("disabled", "disabled");
					pnl.find("style").attr("disabled", "disabled");
				}
			});
		});
	};
	var initSpace = function(variables) {
		var serial = "$" + (xui.serial++);
		var v = $.extend({ xfn:DEF.xfn }, variables);
		xui[serial] = v;

		// 从options中查找局部变量声明, 追加前缀映射为全局变量
		this.find("[data-options]").andSelf().each(function() {
			handleOptions.call(this, "xui." + serial + ".", v);
		});

		var finalProgress = (v.finalProgress || 100) / 100.00;
		var onSpaceLoadSuccess = v.onSpaceLoadSuccess;
		return this.each(function() {
			var self = this;
			var space = $(this);

			// TABS页面, 更新加载进度
			var tabs = space.is(".x-space") && space.panel("options").tabs;
			if (tabs) { space.xcall("xprogress", 50 * finalProgress); }

			var last = new Date().getTime();
			var total = $("[class*=x-]").length;
			var index = 0;

			// 初始化自身
			if (space.is(".x-space")) {
				initElement.call(self, { first:true, type:"panel", xref:{ serial:serial, space:space } });
			} else {
				$.each(space.attr("class").split(/\s+/), function(i, clazz) {
					for (var i = 0, len = $.parser.plugins.length; i < len; i++) {
						var type = $.parser.plugins[i];
						if ("x-" + type == clazz) {
							initElement.call(self, { first:true, type:type, xref:{ serial:serial, space:space } });
							return false;
						}
					};
				});
			}
			// 初始化子元素
			$.each($.parser.plugins, function(i, type) {
				space.find(".x-" + type).each(function() {
					initElement.call(this, { first:true, type:type, xref:{ serial:serial, space:space } });
					// 更新进度,主要是IE8加载较慢,textbox约22ms,combobox约48ms,datagrid约300ms,treegrid约85ms
					// 希望能根据元素个数计算百分比,但实测,视觉上基本看不出什么效果,加不加这段代码都是从30%直接跳到100%
					index ++;
					var curr = new Date().getTime();
					if (tabs && curr - last > 100) {
						space.xcall("xprogress", (50 + index / total / 2 * 100) * finalProgress);
						last = curr;
					}
				});
			});

			// 加载附加数据
			space.find(".x-extra").each(function() {
				var xopts = $.parser.parseOptions(this);
				filterExtraData.call(this, space, xopts);
			});

			if (tabs) {
				var sopts = space.xcall("options");
				// 加载Boolean等从外部传过来的附加数据
				initExtraInput.call(self, space, sopts.extras);
			}

			// 加载成功, TABS页面更新进度为100%
			if (tabs) {
				space.xcall("xprogress", 100 * finalProgress);
				// TABS页面, 记录下成功标记, 用于点击失败页面的菜单时刷新页面
				space.xcall("options").loadSuccess = true; // 加载成功标记
			}

			space.find("form").each(function() {
				var opts = $(this).zoptions();
				if (opts.initValidation != "enabled") {
					$(this).form("disableValidation");
				}
			});

			// 回车查询
			space.on("keydown", ".toolbar-box input", function(e) {
				if (e.keyCode == 13) {
					DEF.xfn.toolbar.search.call(this);
				}
			});

			// 点击打开对话框
			// 这里只处理a/button之类的点击, linkbutton是直接通过xpopup指定的, data-options={xpopup:'xxx'}
			space.on("click", ".click-open-xpopup:not(.x-linkbutton)", function(e) {
				var self = this;
				var me = $(this);
				var xcall = me.data("x-call");
				if (!xcall) {
					// 模拟linkbutton的options调用形式
					me.data("x-call", "open-xpopup");
					me.data("open-xpopup", me.zoptions());
				}
				var type = 'xpopup';
				var def = DEF.onLinkButton[type];
				var bopts = me.xcall("options");
				if (!bopts["click-open-xpopup-init"]) {
					bopts["click-open-xpopup-init"] = true;
					// 模拟linkbutton的options内容
					for (var key in def) {
						if (key == "onClick" || key == "onDblClick") { continue; }
						if (bopts[key] === undefined || bopts[key] == $.fn.linkbutton.defaults[key]) {
							bopts[key] = def[key];
						}
					}
				}
				var datalist = bopts.datalist;
				if (!datalist) { // 没有指定datalist, 查找当前按钮是不是在datagrid/treegrid当中
					var datagrid = me.closest(".datagrid-view").children(".x-datagrid, .x-treegrid");
					if (datagrid.length) {
						datalist = datagrid;
					}
				} else if ($.dt.isString(datalist)) { // 指定了selector, 从当前space当中查找datalist
					datalist = findDatalist.call(self, space, datalist);
				}
				// 绑定xref
				bopts.xref = { space:space, datalist:datalist };
				// 模拟linkbutton的点击事件
				def.onClick.call(self);
			});

			if ($.isFunction(onSpaceLoadSuccess)) { onSpaceLoadSuccess.call(self, { space:space, serial:serial, tabs:tabs }); }
		});
	};
	// 根据按钮查找datalist
	// 先判断当前按钮是不是在popup当中, 再判断是不是在tabs当中
	var findDatalist = function(space, selector) {
		var me = $(this);
		var temp = me.closest(".x-dialog, .x-window").find(selector);
		if (temp.length == 0) {
			temp = me.closest(".x-tabs").find(selector);
		}
		if (temp.length == 0) {
			temp = space.find(selector);
		}
		return temp.length ? temp : undefined;
	};
	var spaceLoadElement = function(elements) {
		var space = $(this);
		var serial = space.xcall("options").xref.serial;

		// 从options中查找局部变量声明, 追加前缀映射为全局变量
		elements.find("[data-options]").andSelf().each(function() {
			handleOptions.call(this, "xui." + serial + ".", xui[serial]);
		});

		elements.each(function() {
			var self = this;
			var me = $(this);

			// 初始化自身
			$.each(me.attr("class").split(/\s+/), function(i, clazz) {
				for (var i = 0, len = $.parser.plugins.length; i < len; i++) {
					var type = $.parser.plugins[i];
					if ("x-" + type == clazz) {
						initElement.call(me[0], { first:true, type:type, xref:{ serial:serial, space:space } });
						return false;
					}
				};
			});
			// 初始化子元素
			$.each($.parser.plugins, function(i, type) {
				me.find(".x-" + type).each(function() {
					initElement.call(this, { first:true, type:type, xref:{ serial:serial, space:space } });
				});
			});

		});
	};

	$.fn.xui = function(variables) {
		if (variables == "init") {
			initTabs.call(this);
		} else if (variables == undefined || $.isPlainObject(variables)) {
			initSpace.call(this, variables || {});
		} else { // 初始化指定的子节点
			spaceLoadElement.call(this, $(variables));
		}
	};

	/**
	$("a").filterOptions("name", "save"); -- <a data-options="name:'save'"></a>
	$("a").filterOptions("url"); -- <a data-options="url:'http://...'"></a>
	**/
	$.fn.filterOptions = function(key, value) {
		var regexp;
		if (typeof(value) == "string") {
			regexp = new RegExp("['\"]?\\b"+key+"\\b[\"']?\\s*:\\s*['\"]"+value+"[\"']\\s*(,|$)", "m");
		} else if (typeof(value) == "boolean" || typeof(value) == "number") {
			regexp = new RegExp("['\"]?\\b"+key+"\\b[\"']?\\s*:\\s*"+value+"\\s*(,|$)", "m");
		} else if (value == null) {
			regexp = new RegExp("['\"]?\\b"+key+"\\b[\"']?\\s*:", "m");
		}
		return this.filter(function() {
			return regexp.test($(this).attr("data-options"));
		});
	};
	$.fn.xcall = function(operate, options) {
		var xcall = this.data("x-call");
		if (xcall) {
			if (!$.fn[xcall] && operate == "options") {
				// 如果xcall不是jQuery插件, 且操作是读取options, 则直接返回options
				// 用于普通a标签打开对话框时提前构造数据模拟linkbutton
				return this.data(xcall);
			} else { // 调用jQuery插件
				if (this.data(DELAY)) { // 有延迟加载数据, 表示该元素还没有初始化
					DEF.plugins[xcall] && DEF.plugins[xcall].call(this[0]); // 先初始化
				}
				return this.eq(0)[xcall](operate, options);
			}
		} else {
			$.log.error("data x-call not found.");
		}
	};
	/**
	space.xfind("xxx"); // find()的加强版
	// 由于dialog弹出后已经跳出原dom结构, 成为<body>的子元素了
	// find()方法就找不到dialog中的元素了, 但这个加强版能找到, 而且不会冲突
	**/
	$.fn.xfind = function(selector) {
		var list = this.find(selector);
		this.each(function() {
			var me = $(this);
			var popts = me.xcall("options");
			if (popts.popups) { // 已初始化的
				for (var type in popts.popups) {
					popts.popups[type].find(selector).each(function() {
						list.push(this);
					});
				}
			}
		});
		return list;
	};
	/**
	 * 刷新
	 * $(xxx).xreload(); // 刷新自已
	 * $(xxx).xreload(true); // 刷新子元素
	 * $(xxx).xreload(true, true); 刷新自己和子元素
	 * 有reload方法并且有url的元素才能刷新
	 * children:true/false, 是否刷新子元素
	 * self:true/false, 是否刷新自己
	 * @author zhaohuihua
	 */
	$.fn.xreload = function(children, self) {
		var me = this;
		var opts = me.xcall("options");
		if (opts.tabs && !opts.loadSuccess) {
			// 如果是tabs页签, 又加载失败了, 则重新加载整个页面
			me.xcall("refresh");
			return;
		}
		// 刷新子元素
		if (children) {
			$.each($.parser.plugins, function(i, key) {
				// 有reload方法并且有url的元素才能刷新
				if (!$.fn[key].methods.reload) { return true; }
				me.find(".x-" + key).filterOptions("url").each(function() {
					var me = $(this);
					me[key]("options").inited = false;
					me[key]("reload");
				});
			});
		}
		// 刷新自已
		if (self || children == undefined) {
			var key = this.data("x-call");
			if (key && $.fn[key].methods.reload) {
				this.filterOptions("url").each(function() {
					var me = $(this);
					me[key]("options").inited = false;
					me[key]("reload");
				});
			}
		}
	};
	// space.xpopups("type", "open", options);
	$.fn.xpopups = function(type, operate, options) {
		var popts = this.panel("options");
		if (!type) { // 查找全部对话框
			var list = [];
			if (popts.popups) { // 已初始化的
				for (var type in popts.popups) {
					$.each(popts.popups[type], function() { list.push(this); });
				}
			}
			this.find(".x-dialog, .x-window").each(function() {
				list.push(this);
			});
			return $(list);
		}
		if (popts.popups && popts.popups[type]) {
			// 对话框已初始化
			if (operate) {
				return popts.popups[type].dialog(operate, options);
			} else {
				return popts.popups[type];
			}
		} else {
			// 对话框未初始化
			var popup = this.find(".x-dialog, .x-window").filterOptions("xpopup", type);
			popup.each(function() {
				xpopup.call(this);
			});
			return popup;
		}
	};

	/*
	// 判断是不是全局变量
	// 为避免coder不执行规范, 不留太多余地, 未声明为全局变量的一律作为内部变量处理
	// 如果需要改变规则, 以全局变量优先, 则:
	$.fn.xui.defaults.globalVariables.push(function(name) {
		if ($.zhh.field(window, name)) { return true; }
	});
	*/
	var isGlobalVariable = function(name) {
		if (BASE.test(name)) { return true; }
		var gvs = DEF.globalVariables;
		for (var i = 0; i < gvs.length; i++) {
			var gv = gvs[i];
			var global = false;
			if (typeof(gv == "string")) { // 前缀
				global = name.startsWith(gv + ".");
			} else if (gv.test) { // 正则表达式
				global = gv.test(name);
			} else if ($.isFunction(gv)) { // 函数
				global = gv(name);
			}
			if (global) { return true; }
		}
		return false;
	};
	var handleOptions = function(prefix, variables) {
		var me = $(this);
		if (me.data("x-call")) {
			return;
		}
		// 从options中查找function声明, 追加前缀
		var text = me.attr("data-options");
		var newer = replaceGlobalVariable(text, prefix, variables);
		// if (text != newer) { $.log.debug(newer); }
		me.attr("data-options", newer);
	};
	var replaceGlobalVariable = function(text, prefix, variables) {
		var attrs = [];
		for (var i = 0, matcher = null; text;) {
			matcher = VARS.exec(text);
			if (!matcher) {
				attrs.push(text.substring(i));
				break;
			}
			var index = matcher.index;
			var name = matcher[4];
			attrs.push(text.substring(i, index));
			attrs.push(matcher[1]);
			attrs.push(matcher[2]);
			attrs.push(matcher[3]);
			if (!isGlobalVariable(name)) { // 非全局变量
				attrs.push(prefix); // 追加前缀
				// 检查局部变量是否存在
				if (!$.zhh.field(variables, name)) {
					$.log.error(name + " not found!");
				}
			}
			attrs.push(name);
			if (!isGlobalVariable(name)) { // 非全局变量
				// 用前缀为$的变量记录下原始函数名
				attrs.push(",");
				attrs.push(matcher[1]);
				attrs.push("$");
				attrs.push(matcher[2]);
				attrs.push(matcher[3]);
				attrs.push("'");
				attrs.push(name);
				attrs.push("'");
			}
			i = index + matcher[0].length;
		}
		return attrs.join("");
	};

	// datalist : datagrid or treegrid ...
	// 根据当前选中行决定toolbar按钮的禁用/启用/隐藏/显示状态
	// 判断按钮状态, e = { datalist, rows }
	var doButtonStateCheck = function(e, options) {
		var result;
		if ($.isFunction(options)) {
			// disableWith or enableWith or showWith or hideWith: function(e) { return e.selected.state == "NORMAL"; }
			result = options.call(this, e);
		} else {
			// disableWith or enableWith or showWith or hideWith: {state:"NORMAL",gender:["MALE","FEMALE"]}
			result = true;
			var list = $.isArray(e.rows) ? e.rows : [e.rows];
			$.each(list, function(i, v) {
				for (var key in options) {
					var targetValues = $.isArray(options[key]) ? options[key] : [ options[key] ];
					var dataValue = $.zhh.field(v, key);
					if ($.inArray(dataValue, targetValues) < 0) {
						result = false; break;
					}
				}
				if (!result) { return false; }
			});
		}
		return result;
	};
	// ignore or include, e = { data }
	var doDataStateCheck = function(e, options) {
		var result;
		if ($.isFunction(options)) {
			// ignore or include: function(e) { return e.data.state == "NORMAL"; }
			result = options.call(this, e);
		} else {
			// ignore or include: {state:"NORMAL",gender:["MALE","FEMALE"]}
			result = true;
			for (var key in options) {
				var targetValues = $.isArray(options[key]) ? options[key] : [ options[key] ];
				var dataValue = $.zhh.field(e.data, key);
				if ($.inArray(dataValue, targetValues) < 0) {
					result = false; break;
				}
			}
		}
		return result;
	};
	var doButtonStateSwitch = function(e) {
		var me = $(this);
		var opts = me.linkbutton("options");
		if (opts.disableWith) {
			var result = doButtonStateCheck.call(this, e, opts.disableWith);
			me.linkbutton( result === true ? "disable" : "enable" );
		} else if (opts.enableWith) {
			var result = doButtonStateCheck.call(this, e, opts.enableWith);
			me.linkbutton( result === true ? "enable" : "disable" );
		} else {
			me.linkbutton( "enable" );
		}
		if (opts.hideWith) {
			var result = doButtonStateCheck.call(this, e, opts.hideWith);
			me[result === true ? "hide" : "show"]();
		} else if (opts.showWith) {
			var result = doButtonStateCheck.call(this, e, opts.showWith);
			me[result === true ? "show" : "hide"]();
		} else {
			me.show();
		}
	};
	// 当选择行数改变, e = { datalist }
	var onDatalistSelectionsChanged = function(e, selections) {
		var me = $(this);
		var buttons = me.xcall("getPanel").find(".datagrid-toolbar .x-linkbutton");
		var single = buttons.filterOptions("selection", "single");
		if (selections.length != 1) {
			single.linkbutton( "disable" );
		} else {
			var evt = $.extend({ rows:selections }, e);
			single.each(function() {
				doButtonStateSwitch.call(this, evt);
			});
		}
	};
	// 当勾选行数改变, e = { datalist }
	var onDatalistCheckedChanged = function(e, checked) {
		var me = $(this);
		var buttons = me.xcall("getPanel").find(".datagrid-toolbar .x-linkbutton");
		var multi = buttons.filterOptions("selection", "multi");
		if (!checked.length) {
			multi.linkbutton( "disable" );
		} else {
			var evt = $.extend({ rows:checked }, e)
			multi.each(function() {
				doButtonStateSwitch.call(this, evt);
			});
		}
	};
	// 检查选中行数是否改变
	var checkDatalistSelections = function(e) {
		var me = $(this);
		var dopts = me.xcall("options");
		var selections = me.xcall("getSelections");
		if (dopts.counter.selections != selections.length) {
			dopts.counter.selections = selections.length;
			onDatalistSelectionsChanged.call(this, e, selections);
		}
		var checked = me.xcall("getChecked");
		if (dopts.counter.checked != checked.length) {
			dopts.counter.checked = checked.length;
			onDatalistCheckedChanged.call(this, e, checked);
		}
	};
	// 截获extra参数
	// datalist: xextra:true
	// datalist: xextra:{fields:['Gender',...], url:'extra.json',method:'GET',data:'extra'}
	// datalist: xextra:{fields:['Gender',...], url:'extra.json',method:'GET',data:'extra',map:{Dept:'body'}}
	// datalist: xextra:['Gender',{field:'areas',key:'areaCode',value:'areaName'},{field:'xxx',fn:onExtraChangedOfXxx}]
	// 'Gender'是简写, 相当于{field:'Gender',key:'key',value:'value'}
	// map:{Dept:'body'}: 将查询结果本身作为extra数据, 仅适用于不分页且不带筛选条件的列表
	//     BUG:如果用户输入条件筛选数据, 那么作为combo的下拉列表也会被过滤掉, 因此不适用于带筛选条件的列表
	// combobox: xextra:'Gender'
	// combobox: xextra:{field:'Gender',prepend:{key:'',value:'全部'}}
	// combobox: xextra:{field:'Gender',ignore:"UNKNOWN"}
	// combobox: xextra:{field:'Gender',ignore:["UNKNOWN"]}
	// combobox: xextra:{field:'Gender',ignore:{type:"OTHER"}} // 忽略掉全部type=OTHER的
	// combobox: xextra:{field:'Gender',ignore:{type:["ANOTHER","OTHER"]}} // 忽略掉全部type=ANOTHER或type=OTHER的
	// combobox: xextra:{field:'Gender',ignore:fn} // fn(item) return true为忽略
	var filterExtraData = function(space, xe, data) {
		var self = this;
		var extras = space.xcall("options").extras;
		if (!xe) { return; }
		if (xe === true) { xe = {}; }
		else if ($.isArray(xe)) { xe = { fields:xe }; }

		var items = {};
		if (data) {
			var n = $.zhh.field(data, xe.data || "extra");
			if (n) {
				for (var k in n) { 
					if (!k) { continue; }
					if ($.isArray(n[k])) { items[k] = n[k]; }
					else if (k != "total") { extras[k] = n[k]; }
				}
			}
			if (xe.map) { // 将指定数据映射为extra数据
				for (var k in xe.map) {
					var v = $.zhh.field(data, xe.map[k]);
					if ($.isArray(v)) { items[k] = v; }
				}
			}
		}
		if (xe.url && !xe.inited) {
			$.ajax({ url:xe.url, method:xe.method || "GET", async:false,
			success:function(data) {
				var n = $.zhh.field(data, xe.data || "extra");
				for (var k in n) { if (k && $.isArray(n[k])) { items[k] = n[k]; } }
				if (xe.map) { // 将指定数据映射为extra数据
					for (var k in xe.map) {
						var v = $.zhh.field(data, k);
						if ($.isArray(v)) { items[xe.map[k]] = v; }
					}
				}
				xe.inited = true;
			}, error:function(e) {
				$.log.error(e);
			}});
		}
		var fields = {};
		$.each(xe.fields || [], function(i, item) {
			var list = [];
			if (typeof(item) == "string") {
				// 简写转换为完整格式
				// 'Gender' -- {field:'Gender',config:{key:'key',value:'value'}}
				fields[item] = { field:item, config:{ key:"key", value:"value" } };
				if (!(item in items)) { items[item] = undefined; }
			} else if ("field" in item) {
				var config = $.extend({ key:"key", value:"value" }, item);
				fields[item.field] = { field:item.field, config:config };
				if (!(item.field in items)) { items[item.field] = undefined; }
			} else {
				for (var key in item) {
					if ($.isFunction(item[key])) {
						var config = $.extend({ key:"key", value:"value", fn:item[key] }, item);
						fields[key] = { field:key, config:config };
						if (!(key in items)) { items[key] = undefined; }
					}
				}
			}
		});

		if ($.isEmptyObject(items)) {
			return;
		}

		var map = {};
		// xextra:[{'AdminState':'State'}], key=AdminState, value=State
		for (var value in items) {
			// 如果没有特殊规则, 采用默认规则
			var opts = fields[value] || { field:value, config:{ key:"key", value:"value" } };
			var config = opts.config;
			var key = opts.field;

			var list = config && config.data ? $.zhh.field(data, config.data) : items[value];
			if (!$.isArray(list)) { continue; }
			var copy = [];
			$.each(list || [], function(i, o) { 
				copy.push($.extend({}, o));
			});
			if (config.fn) { // 调用外部函数
				var temp = { map:{}, list:[], config:$.extend(true, {}, config) };
				var newvalue = config.fn.call(self, temp, copy, data);
				if (newvalue !== false) {
					extras[key] = temp;
				}
			} else {
				extras[key] = { map:{}, list:[], config:$.extend(true, {}, config) };
				// 默认按枚举处理
				// 如: { extra: { Gender:[{key:"MALE",value:"男"},{key:"FEMALE",value:"女"}] }
				DEF.handleExtraData.call(self, extras[key], copy, data);
			}
			map[key] = extras[key];
		};
		initExtraInput.call(self, space, map);
	};
	
	var initExtraInput = function(space, map) {
		var extras = space.xcall("options").extras;
		// 延迟加载关联的下拉框
		setTimeout(function() {
			for (k in map) {
				if (!k || !map[k].list || !map[k].map) { continue; }
				// 替换在当前extra数据加载之前就已经格式化了的数据
				// 例如左右两个datalist, 右侧某一列依赖左侧的extra.Gender
				// 由于加载速度不一样, 如果右侧先加载, 则格式化为占位符
				// 类似于<span data-extra-pending-Gender="{\"value\":\"MALE\",\"options\":{\"field\":\"Gender\"}}"></span>
				// 左侧加载完成后再把占位符替换为真正显示的内容(男)
				space.find("[data-extra-pending-" + k + "]").each(function() {
					var me = $(this);
					var attr = me.zoptions("extra-pending-" + k);
					var text = handleDatalistExtraValue.call(this, attr.value, attr.options, extras);
					me.html(text).removeAttr("data-extra-pending-" + k);
				});
				// 查找关联的下拉框, 填充数据
				space.xfind("input").each(function() {
					var self = this;
					var me = $(this);
					var xcall = $(this).data("x-call");
					if (!xcall) { return true; }
					var opts = me.xcall("options");
					if (typeof (opts.xextra) == "string") {
						opts.xextra = { field:opts.xextra };
					}
					if (!opts.xextra || (opts.xextra.field != k)) { return true; }
					// 兼容之前不规范的写法, 之前ignore/append/prepend与xextra是平级的
					var ignore = opts.xextra.ignore || opts.ignore;
					var append = opts.xextra.append || opts.append;
					var prepend = opts.xextra.prepend || opts.prepend;

					var key = opts.xextra.field || me.attr("textboxname");
					// 无论接口返回的数据是什么, handleExtraData已经处理成统一的格式了
					var config = { key:"key", value:"value", parent:"parent", group:"group" };
					// 没有loadData方法, continue
					if (!$.fn[xcall].methods.loadData) { return true; }
					if (xcall == "tree" || xcall == "treegrid" || xcall == "combotree") {
						opts.idField = config.key;
						opts.treeField = config.value;
						opts.parentField = config.parent;
					} else {
						opts.valueField = config.key;
						opts.textField = config.value;
						opts.groupField = config.group;
					}
					var values = [];
					// 有可能需要增加"全部","请选择"之类
					if (prepend) {
						if ($.isArray(prepend)) {
							$.each(prepend, function(i,v) {
								values.push($.extend({}, v));
							});
						} else {
							values.push($.extend({}, prepend));
						}
					}
					// 复制节点
					$.each(map[key].list, function(i, item){
						var valid = true;
						if (ignore) { // 判断是不是有效
							if ($.isFunction(ignore) || $.isPlainObject(ignore)) {
								// ignore:fn || ignore:{type:["ANOTHER","OTHER"]}
								valid = !doDataStateCheck.call(self, { data:item.original, item:item }, ignore);
							} else { // ignore:["UNKNOWN"]
								var tmp = {};
								tmp[config.key] = ignore;
								valid = !doDataStateCheck.call(self, { data:item }, tmp);
							}
						}
						// 复制节点
						if (valid) { values.push($.extend(true, {}, item)); }
					});
					if (append) {
						if ($.isArray(append)) {
							$.each(append, function(i,v) {
								values.push($.extend({}, v));
							});
						} else {
							values.push($.extend({}, append));
						}
					}
					// 填充数据
					me.xcall("loadData", values);
				});
			}
		}, 100);
	};
	var callEvent = function(event, args) {
		var opts = $(this).xcall("options");
		var xcall = $(this).data("x-call");
		var name = opts["$" + event];
		var serial = opts.xref.serial;
		var fn = name && $.zhh.field(xui[serial], name);
		if (fn) {
			// 调用业务代码(data-options="xxx:fn")
			return fn.apply(this, args);
		} else {
			// 调用默认实现
			return $.fn[xcall].defaults[event].apply(this, args);
		}
	};
	// options = { type:string, first:boolean, xref:json }
	var xdatalist = function(options) {
		var me = $(this);
		var params = {
			loadMsg:"", xref:options.xref, counter:{ selections:0, checked:0 }
		};
		var space = options.xref.space;
		if (space.length) {
			var opts = $.parser.parseOptions(this);
			$.each(["toolbar", "footer"], function(i, key) {
				if (typeof(opts[key]) == "string") {
					var node = space.find(opts[key]);
					if (node.length) { params[key] = node; }
				}
			});

			// 从<th>查找checkbox:true的作为idField
			if (!opts.idField) {
				var checkbox = me.find("thead th").filterOptions("checkbox", true);
				if (checkbox.length) {
					params.idField = $.parser.parseOptions(checkbox[0]).field;
				}
			}

			// 重写loadFilter事件, 植入代码用于截获extra参数
			params.loadFilter = function(data) {
				var opts = $(this).xcall("options");
				if (data) {
					if (opts.xextra) {
						// 调用植入代码
						filterExtraData.call(this, space, opts.xextra, data);
					}
				}
				// 调用业务代码
				var result = callEvent.call(this, "loadFilter", arguments);
				// 复制数据
				// result = [row] or { total:total, rows:rows }
				var list = $.isArray(result) ? result : result && result.rows;
				if (list && opts.eachCopy) {
					eachCopy.call(this, list, opts.eachCopy, data);
				}
				return result;
			};
			// 重写onBeforeLoad事件, 植入代码用于通过form获取查询参数
			// datagrid.onBeforeLoad(param), treegrid.onBeforeLoad(row, params), tree.onBeforeLoad(node, params)
			params.onBeforeLoad = function(dp, tp) {
				var opts = $(this).xcall("options");
				var params = tp || dp; // datagrid,treegrid的params位置不一样
				if (!opts.inited) { params.extra = true; if (opts.url) { opts.inited = true; } }
				params.paging = opts.pagination; // 是否分页
				params.needCount = opts.pagination; // 是否需要统计总数
				var initial = opts.xref.space.xcall("options").initial;
				// fixedParams是每次请求都会追加的参数
				// 与queryParams的区别是, dg.datagrid("load",{}), queryParams会被清除, fixedParams清除不掉
				$.extend(params, initial.fixedParams, opts.fixedParams);
				var form = $(this).xcall("getPanel").find(".datagrid-toolbar form");
				if (form.length) { $.extend(params, form.serializeJson()); }
				// 调用业务代码
				return callEvent.call(this, "onBeforeLoad", arguments);
			};
			params.onLoadSuccess = function() {
				var me = $(this);
				var opts = me.xcall("options");
				var serial = opts.xref.serial;
				checkDatalistSelections.call(this, {datalist:me}); // 调用植入代码
				callEvent.call(this, "onLoadSuccess", arguments); // 调用业务代码
				
				var viewer = me.xcall("getPanel").find(".datagrid-body .datagrid-btable")
				// 从options中查找局部变量声明, 追加前缀映射为全局变量
				viewer.find("[data-options]").each(function() {
					handleOptions.call(this, "xui." + serial + ".", xui[serial]);
				});
				var xref = $.extend({}, opts.xref, { datalist:me });
				viewer.each(function() {
					var self = this;
					var me = $(this);
					// 初始化子元素
					$.each($.parser.plugins, function(i, type) {
						me.find(".x-" + type).each(function() {
							initElement.call(this, { first:true, type:type, xref:xref });
						});
					});

				});
			};
		}
		// 绑定一堆事件, 植入一段代码, 用于检查选中行数是否改变
		var events = "onCheck onUncheck onCheckAll onUncheckAll onSelect onUnselect onSelectAll onUnselectAll";
		$.each(events.split(/\s+/), function(i, event) {
			params[event] = function() {
				var opts = $(this).xcall("options");
				checkDatalistSelections.call(this, {datalist:me}); // 调用植入代码
				return callEvent.call(this, event, arguments); // 调用业务代码
			};
		});
		me.xcall(params);

		initDatalistToolbarButton.call(this);
	};
	// eachCopy配置在datagrid/tree/treegrid选项中
	// eachCopy:[ { xxField:'from.field', yyField:fn(row, field, json, options), '{options}':{arrayToString:true} } ]
	// x-datagrid data-options="url:'users.json',eachCopy:{roleIds:'extra.roleIds.{id}'}"
	// users.json返回数据: { body:[{id:111},{id:222},...], extra:{roleIds:{ 111:[1101,1102],222:[2205] }} }
	// 复制之后, body = [{id:111,roleIds:[1101,1102]},{id:222,roleIds:[2205]},...]
	// arrayToString(如果是数组则转化为字符串) eachCopy:{ '{options}':{arrayToString:'/'}, roleIds:'extra.roleIds.{id}', ... }
	// 复制之后, body = [{id:111,roleIds:"1101/1102"},{id:222,roleIds:"2205"},...]
	var eachCopy = function(rows, fields, json) {
		var self = this;
		var fieldArray = $.isArray(fields) ? fields : [fields];
		$.each(rows || [], function(i, row) {
			$.each(fieldArray, function(i, fields) {
				var options = fields['{options}'] || {};
				for (var k in fields) {
					if (k == '{options}') { continue; }
					var value;
					if ($.isFunction(fields[k])) {
						value = fields[k].call(self, row, k, json, options);
					} else {
						var formatted = $.zhh.format(fields[k], row);
						var data = json;
						if (formatted.startsWith("this.")) {
							data = row; formatted = formatted.substring("this.".length);
						}
						value = $.zhh.field(data, formatted);
						if ($.isArray(value) && options.arrayToString) {
							var s = options.arrayToString === true ? "," : options.arrayToString;
							value = value.join(s);
						}
					}
					row[k] = value;
				}
				if ($.isArray(row.children)) {
					eachCopy.call(self, row.children, fieldArray, json);
				}
			});
		});
	};
	var initDatalistToolbarButton = function() {
		var me = $(this);
		var opts = me.xcall("options");
		var multiCheckCls = opts.multiCheckCls;
		me.xcall("getPanel").find(".datagrid-toolbar .x-linkbutton").each(function() {
			// 将button与datagrid关联起来
			var btn = $(this);
			var xref = $.extend({}, opts.xref, { datalist:me });
			var bopts = btn.linkbutton("options");
			xref.toolbar = btn.closest("datagrid-toolbar");
			xref.form = btn.closest("form");
			initElement.call(this, { type:"linkbutton", xref:xref });
			// 多选按钮需要勾选第1列才能操作, 这个区别不太明显, 因此想加一个标志与其他按钮区分一下
			// 方案1: multi-use-checkbox-icon, 多选按钮的图标统一换成checkbox
			// 方案2: multi-use-red-icon, 多选按钮的图标颜色统一换成红色
			// 方案3: multi-use-check-flag, 多选按钮的文字后面加check标记
			if (bopts.selection == "multi" && multiCheckCls) {
				btn.addClass(multiCheckCls);
			}

			// 根据xaction设定默认的行为, xpopup=打开对话框
			var def = DEF.onToolbarButton[bopts.xaction];
			if (def) {
				for (var key in def) {
					if (bopts[key] === undefined || bopts[key] == $.fn.linkbutton.defaults[key]) {
						bopts[key] = def[key];
					}
				}
			}
		});
	};
	// 植入代码解决th上的xextra/xpopup/xtemplate
	// xextra:true
	// xextra:'Gender'
	// xextra:{field:'Gender',cls:{MALE:'blue',FEMALE:'red'},ignore:['UNKNOWN'],emptyText:'(未分类)',notFoundText:'(已删除)'}
	// xpopup:'popup-name',xoptions:{} // 打开对话框, xoptions选项, 如xoptions:{onAfterFillData:fn,...}
	// xtemplate:'template-type' // 通过template-type指向<script type="template-type"></script>并获取内容
	var originalDatagridParseData = $.fn.datagrid.parseData;
	$.fn.datagrid.parseData = function (element) {
		var me = $(element);
		var opts = me.datagrid("options");
		var space = opts.xref.space;
		if (!space) { return originalDatagridParseData(element); }

		var extras = space.xcall("options").extras;
		var serial = opts.xref.serial;
		var columns = opts.columns;
		$.each(columns, function() {
			$.each(this, function() {
				if (this.formatter) { return true; }
				if (this.xextra) {
					this.formatter = function(value) {
						var o = { field:this.field };
						if (typeof (this.xextra) == "string") {
							o = { field:this.xextra };
						} else if ($.isPlainObject(this.xextra)) {
							o = $.extend({}, o, this.xextra);
						}
						// value有可能是数组
						if (value == undefined || value === "" || value.length == 0) { return o.emptyText || ""; }
						if (!extras[o.field]) { // 找不到附加数据, 则先格式化为占位符
							var tpl = '<span data-extra-pending-{0}="{1}"></span>';
							var json = JSON.stringify({value:value, options:o})
				            	.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\\/g, '&#92;')
								.replace(/"/g, '&quot;').replace(/'/g, '&#39;');
							return $.zhh.format(tpl, o.field, json);
						}
						return handleDatalistExtraValue.call(this, value, o, extras);
					};
				} else if (this.xpopup) {
					var xpopup = this.xpopup;
					var options = this.xpopupOptions || this.xoptions || {};
					this.formatter = function(value) {
						if (value || value === 0 || value === false) {
							var temp = $.extend(true, {}, options);
							temp.xpopup = xpopup;
							// JSON.stringify(options) 不会包含值为函数的属性
							var text = JSON.stringify(temp)
								// "$onOpen":"fnName" -- "onOpen":fnName
								.replace(/(['"])\$([\$\w]+)(["'])\s*(:)\s*['"]([\$\w]+)["']/, "$1$2$3$4$5")
				            	.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\\/g, '&#92;')
								.replace(/"/g, '&quot;').replace(/'/g, '&#39;');
							var tpl = '<a class="click-open-xpopup" data-options="{1}">{0}</a>';
							return $.zhh.format(tpl, value, text);
						}
					};
				} else if (this.xtemplate) {
					var type = this.xtemplate;
					this.formatter = function(value, row, index) { // tree的参数只有row
						var data = row || value;
						if (!data.i) { data.i = index; }
						return DEF.getTemplateContent(space, type, data);
					};
				}
			});
		});
		return originalDatagridParseData(element);
	};
	// now:true  默认设置为当前时间
	// ignoreYear:true  月日格式
	// monthMode:true  年月格式
	// startTime:true  时间部分设置为00:00:00
	// endTime:true  时间部分设置为23:59:59
	// options = { type:string, first:boolean, xref:json, copyOptions:json }
	var xdatetime = function(options) {
		var me = $(this);
		var params = $.extend({ xref:options.xref }, options.copyOptions);;
		var tmpOpts = $.parser.parseOptions(this);
		if (tmpOpts.now) { params.value = Dates.format(new Date()); }
		params.onShowPanel = function() {
			var me = $(this);
			var opts = me.xcall("options");
			doShowHideYear.call(this, !opts.ignoreYear);
			if (tmpOpts.monthMode) { // 年月格式
				upgradeToMonthMode.call(this);
			}
			// 调用业务代码
			return callEvent.call(this, "onShowPanel", arguments);
		};
		if (tmpOpts.ignoreYear) { // 月日格式
			params.formatter = function(date) {
				if (typeof(date) == "string") {
					date = Dates.parse(date, new Date());
				}
				return Dates.format(date, tmpOpts.formatPattern || "MM-dd");
			};
			params.parser = function(date) {
				return Dates.parse(date, new Date());
			};
		} else if (tmpOpts.monthMode) { // 年月格式
			params.formatter = function(date) {
				if (typeof(date) == "string") {
					date = Dates.parse(date, new Date());
				}
				return Dates.format(date, tmpOpts.formatPattern || "yyyy-MM");
			};
			params.parser = function(date) {
				if (date && typeof(date) == "string") {
					return Dates.parse(date + "-01", new Date());
				} else {
					return Dates.parse(date, new Date());
				}
			};
		}
		if (tmpOpts.ignoreYear || tmpOpts.startTime || tmpOpts.endTime) {
			params.onChange = function(newvalue, oldvalue) {
				var me = $(this);
				var opts = me.xcall("options");
				var date = Dates.parse(newvalue);
				if (date && !isNaN(date)) {
					if (opts.ignoreYear) {
						date.setFullYear(new Date().getFullYear());
					}
					if (opts.endTime) {
						date.setHours(23);
						date.setMinutes(59);
						date.setSeconds(59);
						date.setMilliseconds(999);
					} else if (opts.startTime) {
						date.setHours(0);
						date.setMinutes(0);
						date.setSeconds(0);
						date.setMilliseconds(0);
					}
					$(this).xcall("textbox").parent().find(".textbox-value").val(Dates.format(date));
				}
				// 调用业务代码
				return callEvent.call(this, "onChange", arguments);
			};
		}
		me.xcall(params);
	};
	var doShowHideYear = function(show) {
		var me = $(this);
		var panel = me.xcall("panel");
		var calendar = me.xcall("calendar");
		var copts = calendar.calendar("options");
		var years = panel.find(".calendar-prevyear, .calendar-nextyear, .calendar-menu-year-inner");
		var title = panel.find(".calendar-title .calendar-text");
		if (show) {
			years.show();
			title.text(copts.months[copts.month - 1] + " " + copts.year);
		} else {
			years.hide();
			title.text(copts.months[copts.month - 1]);
		}
	};
	var upgradeToMonthMode = function () {
		var me = $(this);
		var panel = me.datebox("panel");
		panel.find(".calendar-text").trigger("click"); // 触发click事件弹出月份层
		var opts = me.datebox("options");
		if (!opts.monthUpgraded) {
			opts.monthUpgraded = true;
			setTimeout(function() {
				panel.find("div.calendar-menu-month-inner td")
				.unbind("click.month.calendar")
				.bind("click.month.calendar", function(e) {
					e.stopPropagation(); // 禁止冒泡执行easyui给月份绑定的事件

					// 选中当前单元格
					var td = $(this).closest(".calendar-menu-month");
					if (td.hasClass("calendar-disabled")) {
						return;
					}
					td.closest(".calendar-menu-month-inner")
						.find(".calendar-menu-month").removeClass("calendar-selected");
					td.addClass("calendar-selected");

					var menu = panel.find(".calendar-menu");
					var year = menu.find(".calendar-menu-year").val();
					var month = td.attr("abbr");
					if (!isNaN(year)) {
						year = parseInt(year);
						month = parseInt(month);
					}
					var calendar = me.xcall("calendar");
					var copts = calendar.calendar("options");
					var oldValue = copts.current;
					copts.current = new Date(year, month - 1, 1);
					copts.onSelect.call(calendar.get(0), copts.current);
					opts.onSelect.call(me.get(0), copts.current);
					if (!oldValue || oldValue.getTime() != copts.current.getTime()) {
						opts.onChange.call(me, copts.current, oldValue);
					}
					if (copts.year != year || copts.month != month) {
						copts.year = year;
						copts.month = month;
					}
				});
			}, 10);
		}
	};
	// options = { type:string, first:boolean, xref:json, copyOptions:json }
	var xtextbox = function(options) {
		var me = $(this);
		var params = $.extend({ xref:options.xref }, options.copyOptions);
		var tmpOpts = $.parser.parseOptions(this);
		if (tmpOpts.pinyin) {
			params.onChange = function(value) {
				var opts = $(this).xcall("options");
				// 处理拼音
				if (value) {
					$.post(opts.pinyin.url, {text:value}, function(json) {
						$.each(["short", "full", "mark"], function(i, type) {
							if (opts.pinyin[type]) {
								me.closest("form").find(opts.pinyin[type]).xcall("setValue", json.body[type]);
							}
						});
					});
				} else {
					$.each(["short", "full", "mark"], function(i, type) {
						if (opts.pinyin[type]) {
							me.closest("form").find(opts.pinyin[type]).xcall("setValue", "");
						}
					});
				}
				return callEvent.call(this, "onChange", arguments); // 调用业务代码
			};
		}
		me.xcall(params);
	};
	// options = { type:string, first:boolean, xref:json, copyOptions:json }
	var xlinkbutton = function(options) {
		var me = $(this);
		var params = $.extend({ xref:options.xref }, options.copyOptions);;
		var bopts = $.parser.parseOptions(this);
		if (bopts.selection == "single" || bopts.selection == "multi") {
			params.disabled = true;
		}
		for (var type in DEF.onLinkButton) { // linkbutton插件
			if (bopts[type]) {
				var def = DEF.onLinkButton[type];
				if (type == "xaction") {
					def = DEF.onLinkButton.xaction[bopts[type]];
				}
				for (var key in def) {
					if (bopts[key] === undefined || bopts[key] == $.fn.linkbutton.defaults[key]) {
						params[key] = def[key];
					}
				}
				break; // 插件互斥
			}
		}
		me.xcall(params);
		if (bopts.showWith || bopts.hideWith) {
			btn.hide();
		}
	};
	// options = { type:string, first:boolean, xref:json, copyOptions:json }
	var xpopup = function(options) {
		var me = $(this);
		if (options && options.first === true) {
			delete options.first;
			// 延迟加载
			me.data(DELAY, options);
			return;
		}

		if (me.data(DELAY)) {
			options = me.data(DELAY);
			me.removeData(DELAY);
		}

		var space = options.xref.space;
		var params = $.extend({ xref:options.xref }, options.copyOptions);;
		// 重写onOpen事件, 植入代码用于initValidation的判断
		params.onOpen = function() {
			var opts = $(this).xcall("options");
			if (opts.xref.form && opts.initValidation != "enabled") {
				opts.xref.form.form("disableValidation");
			}
			// 调用业务代码
			return callEvent.call(this, "onOpen", arguments);
		};
		me[options.type](params);

		var dopts = me[options.type]("options");
		var form = me.find("form");
		if (form.length) {
			dopts.xref.form = form;
		}

		// 将对话框绑定到space上
		// 以便通过space.xpopups(xxx)引用, 和当space销毁时销毁对话框
		var type = dopts.xpopup;
		if (type && space.length) {
			var popts = space.panel("options");
			if (!popts.popups) { popts.popups = {}; }
			popts.popups[type] = me;
		}

		// me.dialog("dialog")  or  me.window("window")
		me[options.type](options.type).find(".x-linkbutton").each(function() {
			// 将button与dialog关联起来
			var btn = $(this);
			// 对话框中datagrid
			if (btn.closest(".datagrid-toolbar").length) { return; }
			var xref = $.extend({ popup:me }, options.xref);
			if (form.length > 0) { xref.form = form; }
			initElement.call(this, { type:"linkbutton", xref:xref });

			var bopts = btn.xcall("options");
			// 根据xaction设定默认的行为
			var type = bopts.xaction;
			if (type) {
				if (DEF.onPopupButton[type]) {
					// 绑定点击事件
					for (var key in DEF.onPopupButton[type]) {
						if (bopts[key] === undefined || bopts[key] == $.fn.linkbutton.defaults[key]) {
							bopts[key] = DEF.onPopupButton[type][key];
						}
					}
				} else {
					$.log.error("$.fn.xui.defaults.onPopupButton[" + type + "] not found");
				}
			}
		});
	};

	// 打开对话框时是否立即启动校验, 默认='disabled', 表示打开时不校验, 提交时再启动校验, 体验较好
	// initValidation:"disabled"
	$.fn.dialog.defaults.initValidation = "disabled";

	var DEF = $.fn.xui.defaults = {
		filterExtraData:filterExtraData,
		callEvent:callEvent,
		// globalVariables: string | RegExp | Function
		globalVariables: ["window", "xui"],
		pageContentFilter: /<body[^>]*>((.|[\r\n])*)<\/body>/im, // 加载页面内容的过滤器
		checkAuthorized: function(data) { // 检查用户是否登录
		},
		// 处理附加数据的默认函数
		// e = { list:[], map:{}, config:{ key:"key", value:"value", parent:"parent" } }
		// data = [{key:"MALE",value:"男"},{key:"FEMALE",value:"女"}]
		// zhaohuihua, 2017-02-10: 之前版本, e.list保存的是原始数据,
		// 为简化后续处理, 修改为统一保存 { key:"...", value:"...", parent:"...", group:"...", original:this }
		handleExtraData: function(e, extra, data) {
			var k = e.config.key;
			var v = e.config.value;
			var p = e.config.parent;
			var g = e.config.group;
			$.each(extra, function() {
				if (!this[k]) { return true; }
				var key = $.zhh.field(this, k);
				var value = $.zhh.field(this, v);
				var parent = p && $.zhh.field(this, p);
				var group = g && $.zhh.field(this, g);
				e.list.push({ key:key, value:value, parent:parent, group:group, original:this });
				e.map[key] = value;
			});
		},
		// 根据类型获取模板内容
		getTemplateContent: function(space, type, data) {
			return space.find('script[type="'+type+'"]').html();
		},
		// cls = {MALE:'blue',FEMALE:'orange'}, 根据value查找映射关系
		// cls = true 转换为 '{field}{value}'
		// cls = '{field}{value}', 根据占位符格式化字符串, 如field=Gender,value=MALE, 则返回GenderMALE
		generateExtraClassName: function(cls, field, value) {
			if (!cls) {
				return null;
			} else if ($.isPlainObject(cls)) { // cls:{MALE:'blue',FEMALE:'orange'}
				if (cls[value]) {
					return cls[value];
				} else {
					return null;
				}
			} else { // cls:true 或 cls:'{field}_{value}'
				var pattern = $.dt.isString(cls) ? cls : "{field}_{value}";
				return $.zhh.format(pattern, {field:field, value:value});
			}
		},
		isJsonSuccess: function(json) {
			return json && json.code == "SUCCESS";
		},
		plugins: {
			datagrid: xdatalist,
			treegrid: xdatalist,
			dialog: xpopup,
			window: xpopup,
			linkbutton: xlinkbutton,
			textbox: xtextbox,
			datebox: xdatetime,
			datetimebox: xdatetime,
		},
		xfn: { // 全局通用方法
			toolbar: {
				// toolbar的combo的onChange触发查询
				// x-combotree data-options="onChange:xfn.toolbar.search,..."
				search: function() {
					$(this).closest("form").find(".x-linkbutton").filterOptions("xaction", "search").trigger("click");
				}
			},
			popup: {
				
			},
			combobox: {
				// combobox本地筛选, 一定要设置mode:'local'才会生效
				// x-combobox data-options="mode:'local',filter:xfn.combobox.filter,..."
				filter: function(q, row) {
					if (!q) { return true; }
					var opts = $(this).xcall('options');
					var v = row[opts.valueField];
					var t = row[opts.textField];
					var g = row[opts.groupField];
					return v && v.indexOf(q) == 0 || t && t.indexOf(q) >= 0 || g && g.indexOf(q) >= 0;
				}
			},
			extra: {
				// 将extra返回的列表进行分组, 将parent的value作为group, 适合用于combobox按group展示
				// 注意: 只适用于2级数据, 例如省市
				// xextra:{field:'Area',key:'areaCode',value:'areaName',parent:'parentCode',fn:xfn.extra.group}
				// x-combobox data-options="xextra:'Area',groupField:'group',mode:'local',filter:xfn.combobox.filter,..."
				group: function(e, extra, data) {
					var key = e.config.key;
					var value = e.config.value;
					var parent = e.config.parent;
					$.each(extra, function() {
						if (!this[key]) { return true; }
						e.map[this[key]] = this[value];
					});
					var tree = $.fn.tree.parser(extra, { tid:key, parent:parent, text:value, upgrade:true });
					$.each(tree, function() {
						var p = this;
						$.each(p.children || [], function() {
							e.list.push({ key:this[key], value:this[value], parent:this[parent], group:p[value], original:this });
						});
					});
				}
			}
		},
		onLinkButton: {
			xclick: {
				onClick:function() {
					var self = this;
					// 如果不延时, 当按钮位于datalist行中, 则触发点击事件时, datalist还没有选中当前行
					setTimeout(function() {
						var btn = $(self);
						var e = doLinkButtonCreateEvent.call(self);
						var bopts = btn.xcall("options");
						var fn = bopts.xclick;
						var close = true;
						if ($.isFunction(fn)) {
							close = fn.call(self, e);
						}
						if (close !== false && e.popup) {
							e.popup = e.popup.xcall("close");
						}
					}, 20);
				}
			},
			// data-options="xpopup:'details'"  打开'详情'对话框
			// data-options="xpopup:'details-{type}'" 根据选中行数据打开不同的'详情'对话框
			//     如type:MODULE/GROUP/MENU, 分别对应details-MODULE/details-GROUP/details-MENU; 如果指定的对话框不存在, 会查找details-default
			// data-options="xpopup:'details',copy:{title:true,icon:true}"  打开'详情'对话框, 并从按钮上复制标题和图标
			// data-options="xpopup:'create',fillData:false"  打开'创建'对话框, 不要加载数据
			// data-options="xpopup:'delete',fillData:false,onAfterFillData:onAfterFillDataOfXxx" // 打开'删除'对话框并由具体业务层处理表单填充
			xpopup: {
				onClick:function() {
					var self = this;
					// 如果不延时, 当按钮位于datalist行中, 则触发点击事件时, datalist还没有选中当前行
					setTimeout(function() { onClickButtonOfOpenPopup.call(self); }, 20);
				},
				onBeforeOpen: function(e) { // 打开对话框之前的事件, return false 可以阻止对话框打开
					// 这里的e只有datalist, 没有对话框相关的引用, 因为对话框可能还没有初始化
				},
				onBeforeFillData: function(e) { // 填充数据之前的事件
				},
				onAfterFillData: function(e) { // 点击按钮打开对话框时的事件
				},
				onAfterOpen: function(e) {
				},
				dataFinder: function(e, options) {
					return { selected:e.selected, checked:e.checked };
				},
				copy: { title:false, icon:false }, // 从按钮上复制标题和图标
				// 打开对话框时是否立即启动校验, 默认='disabled', 表示打开时不校验, 提交时再启动校验, 体验较好
				initValidation:"disabled",
				// 打开对话框时是否加载选中行数据
				fillData:true,
				// 打开对话框时是否清空form
				clearOnOpen:true,
				// 允许选择几行, 默认:不控制,单行:single,多行:multi, 仅适用于datalist的ToolbarButton
				selection:undefined,
				// 以下选项仅适用于e.selected或e.checked不为空的情况
				// 根据当前选中行决定toolbar按钮的禁用/启用/隐藏/显示状态
				enableWith:undefined,
				disableWith:undefined,
				showWith:undefined,
				hideWith:undefined
			},
			xaction: {
				back: {
					onClick:function() {
						var btn = $(this);
						var bopts = btn.xcall("options");
						var space = bopts.xref && bopts.xref.space;
						space.xcall("xclose");
					}
				}
			}
		},
		// data-options="xaction:'xxx'"  执行$.fn.xui.defaults.onToolbarButton.xxx, 可用于扩展
		onToolbarButton: { // 点击工具栏的按钮
			reset: { // 重置搜索条件
				onClick:function() {
					var btn = $(this);
					var bopts = btn.xcall("options");
					bopts.xref.form.form("clear");
					bopts.xref.datalist.xcall("load", {});
				}
			},
			search: { // 提交搜索条件
				onClick:function() {
					var btn = $(this);
					var bopts = btn.xcall("options");
					var form = bopts.xref.form;
					form.form("enableValidation");
					if (form.form("validate")) {
						// 通过onBeforeLoad提交参数
						// var params = form.serializeJson();
						// bopts.xref.datalist.xcall("load", params);
						bopts.xref.datalist.xcall("load");
					}
				}
			}
		},
		onPopupButton: { // 点击对话框的按钮
			close: { // 关闭
				onClick: function() {
					var btn = $(this);
					var bopts = btn.xcall("options");
					var popup = bopts.xref.popup;
					popup.xcall("close");
				}
			},
			prev: { // 加载上一行
				onClick: function() {
					doDatalistNextPrev.call(this, -1);
				}
			},
			next: { // 加载下一行
				onClick: function() {
					doDatalistNextPrev.call(this, +1);
				}
			},
			submit: { // 提交
				onClick: function() {
					var btn = $(this);
					var bopts = btn.xcall("options");
					var popup = bopts.xref.popup;
					var form = bopts.xref.form;
					var space = bopts.xref.space;

					var popts = popup.xcall("options");
					var datalist = popts.xref.datalist;
					var e = { space:space, form:form, popup:popup, datalist:datalist };
					if (datalist) {
						e.selected = datalist.xcall("getSelected");
						e.checked = datalist.xcall("getChecked");
					}
					bopts.onReady.call(this, e, doSubmitExec);

					return false;
				},
				onReady: function(e, doSubmit) { // 准备提交, 用于插件扩展
					doSubmit.call(this, e);
				},
				onBefore: function(e) { // 表单提交前事件, 用于具体业务, 返回false将终止表单提交
					return true;
				},
				dataFilter: function(e) { // 解析响应数据, 用于基础业务和插件扩展
					return e.data;
				},
				isSuccess: function(e) { // 判断提交是否成功, 用于基础业务和插件扩展
					return DEF.isJsonSuccess.call(this, e.data);
				},
				onFinish: function(e, doFinish) { // 提交完成, 用于插件扩展
					doFinish.call(this, e);
				},
				onSuccess: function(e) { // 提交成功的事件, 用于具体业务, return false可以取消默认的通知事件
				},
				onError: function(e) { // 提交失败的事件, 用于具体业务, return false可以取消默认的通知事件
				},
				onMessage: function(e) { // 通知事件, 用于基础业务和插件扩展
					if (!e.success && e.xhr) {
						$.log.error(e);
					}
				},
				closeOnSuccess:true, // 提交成功后是否关闭对话框
				clearOnSuccess:false, // 提交成功后是否清空表单
				reloadOnSuccess:true, // 提交成功后是否重新加载列表
				closeOnError:false, // 提交失败后是否关闭对话框
				clearOnError:false, // 提交失败后是否清空表单
				reloadOnError:false, // 提交失败后是否重新加载列表
				validate:true, // 提交前是否需要验证
				message:undefined
			}
		}
	};

	var onClickButtonOfOpenPopup = function() {
		var btn = $(this);
		var e = doLinkButtonCreateEvent.call(this);
		var bopts = btn.xcall("options");
		var result = bopts.onBeforeOpen.call(this, $.extend({}, e));
		if (result === false) { return; }

		var data = { selected:e.selected, checked:e.checked };
		if (bopts.dataFinder) {
			data = bopts.dataFinder.call(this, e, bopts);
			if (!("selected" in data) && !("checked" in data)) { data = { selected:data }; }
		}

		// 查找对话框
		var ph = /\{[\-\w]+\}/; // 是否存在占位符placeholder
		var target = bopts.xpopup;
		if (ph.test(bopts.xpopup)) {
			if (data.selected) {
				target = $.zhh.format(target, data.selected);
			} else {
				$.log.error("有占位符的按钮应保证有选中行数据! xpopup=" + bopts.xpopup);
				return;
			}
		}
		var popup = bopts.xref.space.xpopups(target);
		if (!popup.length && ph.test(bopts.xpopup)) {
			// 如果指定的对话框不存在, 将占位符替换为default再尝试查找
			var rpl = new RegExp(ph.source, "g");
			popup = bopts.xref.space.xpopups(bopts.xpopup.replace(rpl, "default"));
		}
		if (!popup.length) {
			$.log.error("popup not found! options=" + bopts.xpopup + ", target=" + target + ".");
			return;
		}
		var popts = popup.xcall("options");

		// 传递参数
		bopts.xref.target = popup;
		popts.xref.launcher = btn;
		popts.xref.datalist = e.datalist;
		popts.xref.selected = data.selected;
		popts.xref.checked = data.checked;

		e.popup = popup; e.form = popts.xref.form;

		doPopupFillData.call(this, $.extend({}, e));

		// 控制按钮的禁用/启用/隐藏/显示
		var buttons = popup.xcall("window").find("a.x-linkbutton");
		var de = buttons.filterOptions("disableWith").add(buttons.filterOptions("enableWith"));
		var hs = buttons.filterOptions("hideWith").add(buttons.filterOptions("showWith"));
		if (!data.selected) {
			de.linkbutton( "disable" );
			hs.hide();
		} else {
			de.add(hs).each(function() {
				doButtonStateSwitch.call(this, { rows:[data.selected] });
			});
		}

		popup.xcall("open");

		if (bopts.copy) { // copy:{title:true,icon:true}  copy:{title:"标题",icon:"fa fa-icon"}
			if (bopts.copy.title) { // 设置或复制标题
				var title = bopts.copy.title == true ? btn.text() : bopts.copy.title;
				popup.xcall("setTitle", title);
			}
			if (bopts.copy.icon) { // 设置或复制图标
				var icon = bopts.copy.icon == true ? bopts.iconCls : bopts.copy.icon;
				popup.xcall("setIconCls", icon);
			}
		}
		if (bopts.operate) { // operate="create" / operate="update"
			if (e.form) { // 根据operate设置action
				var action = e.form.attr("data-" + bopts.operate + "-action");
				action && e.form.attr("action", action);
			}
			// <div class="show-operate show-create"></div>
			// <div class="show-operate show-update"></div>
			popup.parent().find(".show-operate:not(.show-"+bopts.operate+")").hide().addClass("hide");
			popup.parent().find(".show-operate.show-"+bopts.operate).show().removeClass("hide");
		}

		bopts.onAfterOpen.call(this, $.extend({}, e));

		popup.xcall("center");
	};

	var doDatalistNextPrev = function(to) {
		var btn = $(this);
		var bopts = btn.xcall("options");
		var popup = bopts.xref.popup;
		var popts = popup.xcall("options");
		var datalist = popts.xref.datalist;
		// 获取所有行
		var rows = datalist.xcall("getRows");
		if (rows && rows.length > 1) {
			// 获取选中行
			var selected = datalist.xcall("getSelected");
			var index = 0;
			if (selected) {
				// 获取当前序号
				var curr = datalist.xcall("getRowIndex", selected);
				// 计算新的序号
				index = curr + to;
				if (index < 0) { index = rows.length - 1; }
				else if (index >= rows.length) { index = 0; }
			}
			// 选中新的数据行
			datalist.xcall("unselectAll").xcall("selectRow", index);
			var launcher = popts.xref.launcher.get(0);
			var e = doLinkButtonCreateEvent.call(launcher, { popup:popup });
			// 加载新数据
			doPopupFillData.call(launcher, e);
		}
	};
	// e.popup    1.options.popup    2.btn.xref.popup     3.closest(dialog)
	// e.datalist 1.options.datalist 2.find(btn.datalist) 3.btn.xref.datalist  4.popup.xref.datalist
	// e.selected/e.checked  1.datalist.selected  2.popup.xref.selected  3.initial.selected
	// 对话框dialog-button的btn的e.datalist指向外部datalist
	// 对话框内部的datagrid的btn的e.datalist指向内部datalist
	var doLinkButtonCreateEvent = function(options) {
		var btn = $(this);
		var bopts = btn.xcall("options");
		options = options || {};

		// popup
		var popup = options.popup || bopts.xref.popup;
		if (!popup) {
			var dialog = btn.closest(".window-body");
			if (dialog.length) { popup = dialog; }
		}
		// datalist
		var datalist = options.datalist;
		if (!datalist && bopts.datalist) { // 在按钮上通过datalist指定selector
			var tmp;
			if (popup) {
				tmp = popup.find(bopts.datalist);
			}
			if (!tmp || tmp.length == 0) {
				tmp = space.find(bopts.datalist);
			}
			if (tmp.length) { datalist = tmp; }
		}
		if (!datalist) { datalist = bopts.xref.datalist; }
		if (!datalist && popup) {
			datalist = popup.xcall("options").xref.datalist;
		}
		// launcher
		var launcher = btn;
		if (popup) { launcher = popup.xcall("options").xref.launcher; }
		// form
		var form = btn.closest("form");
		if (form.length == 0) {
			form = popup && popup.xcall("options").xref.form || undefined;
		}
		var e = { popup:popup, form:form, datalist:datalist, launcher:launcher };
		var space = bopts.xref.space;
		var initial = space.xcall("options").initial;
		if (initial && initial.fixedParams) {
			e.fixedParams = initial.fixedParams;
		}
		if (e.popup && !bopts.datalist) {
			e.selected = e.popup.xcall("options").xref.selected;
			e.checked = e.popup.xcall("options").xref.checked;
		} else if (e.datalist) {
			e.selected = e.datalist.xcall("getSelected");
			e.checked = e.datalist.xcall("getChecked");
		} else if (initial) {
			e.selected = initial.selected;
			e.checked = initial.checked;
		}
		return e;
	};
	var doPopupFillData = function(e) {
		var btn = $(this);
		var bopts = btn.xcall("options");
		var popup = e.popup;
		var popts = popup.xcall("options");
		var form = e.form;
		bopts.onBeforeFillData.call(this, $.extend({}, e));

		if (form) {
			if (bopts.clearOnOpen) {
				form.form("clear");
			}
			// 填充initial.fixedParams固定参数
			var tpl = '<input type="hidden" name="{0}" value = "{1}"/>';
			if (e.fixedParams) {
				var html = [];
				for (key in e.fixedParams) {
					form.find("input[name='" + key + "']").remove();
					html.push($.zhh.format(tpl, key, e.fixedParams[key]));
				}
				form.append(html.join(""));
			}
			// 判断是否需要填充数据
			var fillData = bopts.fillData;
			// 填充选中行数据
			// fillData:true
			// 自定义的fillData, selection=multi会自动创建多个input[hidden], selection=single或者未定义, 只会加载数据
			// fillData:'formDataField'
			// fillData:['formDataField1','formDataField2']
			// fillData:{rowDataField:'formDataField',i:'.records'}
			// fillData:{rowDataField1:'formDataField1',rowDataField2:'formDataField2',i:'.records'}
			var data = { selected:e.selected, checked:e.checked };
			if (bopts.dataFinder) {
				data = bopts.dataFinder.call(this, e, bopts);
				if (!("selected" in data) && !("checked" in data)) { data = { selected:data }; }
			}

			if (data.selected && fillData === true) {
				var selected = $.extend({}, data.selected);
				// 解决treegrid的children过多时会导致执行很慢的问题
				if ($.isArray(selected.children)) {
					delete selected.children;
				}
				form.form("load", selected);
			} else if (typeof(fillData) == "string" || $.isPlainObject(fillData) || $.isArray(fillData)) {
				var batch = $.isArray(fillData) ? fillData : [fillData];
				if (bopts.selection == "multi") { // 多选操作按钮
					var checked = data.checked || [];
					$.each(batch, function(i, item) {
						var opts = toOptions(item);
						for (var key in opts) {
							if (key != "i") { form.find("input[name='" + opts[key] + "']:not(.textbox-value)").remove(); }
						}
						if (checked.length == 0) { return true; }
						var html = [];
						$.each(checked, function(){
							for (key in opts) {
								if (key == "i") {
									form.find(opts[key]).text(checked.length);
								} else if (this[key]) {
									html.push($.zhh.format(tpl, opts[key], this[key]));
								}
							}
						});
						form.append(html.join(""));
					});
				} else { // 单选操作按钮
					var selected = data.selected || {};
					$.each(batch, function(i, item) {
						var opts = toOptions(item);
						var data = {};
						for (var key in opts) {
							if (selected[key]) { data[opts[key]] = selected[key]; }
						}
						form.form("load", data);
					});
				}
			}
		}
		bopts.onAfterFillData.call(this, $.extend({}, e));
	};


	var doSubmitExec = function(e) {
		var self = this;
		var btn = $(this);
		var form = e.form;
		var bopts = btn.linkbutton("options");
		if (bopts.validate !== false) {
			form.form("enableValidation");
		}
		if (form && bopts.fillForm) {
			form.form("load", bopts.fillForm);
		}

		var msg = bopts.message || $.trim(btn.text());

		// 提交前禁用按钮
		btn.linkbutton("disable");
		form.form("submit", {
			onSubmit: function() {
				// 验证表单
				var valid = e.form.form("validate");
				// 触发表单提交前事件
				if (valid) { valid = bopts.onBefore.call(self, $.extend({}, e)); }
				// 如果验证未通过, 启用按钮
				if (valid === false) { btn.linkbutton("enable"); }
				return valid; // 返回false终止表单提交
			},
			success: function(data) {
				if ($.dt.isString(data) && data.startsWith("{") && data.endsWith("}")) {
					data = JSON.parse(data);
				}
				btn.linkbutton("enable");
				var evt = $.extend({data:data}, e);
				// 解析数据
				evt.data = bopts.dataFilter.call(self, evt);
				// 判断是否成功
				evt.success = bopts.isSuccess.call(self, evt) !== false;
				evt.message = msg;
				evt.suffix = true;
				bopts.onFinish.call(self, evt, doSubmitFinally);
			},
			onLoadError: function(xhr, status, thrown) {
				btn.linkbutton("enable");
				var evt = $.extend({ success:false, message:msg, suffix:true, xhr:xhr, status:status, thrown:thrown}, e);
				bopts.onFinish.call(self, evt, doSubmitFinally);
			}
		});
	};
	var doSubmitFinally = function(e) {
		var btn = $(this);
		var bopts = btn.xcall("options");
		var type = e.success ? "Success" : "Error";
		// 关闭对话框
		var close = bopts["closeOn" + type];
		if (close && e.popup) {
			e.popup.xcall("close");
		}
		// 清空表单
		var clear = bopts["clearOn" + type];
		if (clear && e.form) {
			e.form.form("clear").form("disableValidation");
			var popts = e.form.form("options");
			if (popts.initValidation != "enabled") {
				e.form.form("disableValidation");
			}
		}
		// 重新加载列表
		var reload = bopts["reloadOn" + type];
		if (reload && e.datalist) {
			e.datalist.xcall("options").inited = false;
			e.datalist.xcall("reload");
		}
		// 通知和最终回调
		var result;
		if (e.success) {
			result = bopts.onSuccess.call(this, e);
		} else {
			result = bopts.onError.call(this, e);
		}
		// return false可以取消默认的通知事件, return string可以替换默认的通知内容
		if (result !== false) {
			if (typeof(result) == "string") {
				e.message = result;
				e.suffix = false;
			}
			bopts.onMessage.call(this, e);
		}
	};





	/**
	 * -----------------------------------------------------------
	 * textbox支持formatter和xextra属性
	 * -----------------------------------------------------------
	 * <input class="x-textbox" name="gender" data-options="readonly:true,formatter:genderToText"/>
	 * <input class="x-textbox" name="gender" data-options="readonly:true,xextra:true"/>
	 * <input class="x-textbox" name="gender" data-options="readonly:true,xextra:'Gender'"/>
	 * <input class="x-textbox" name="gender" data-options="readonly:true,xextra:{field:'Gender',cls:{UNKNOWN:red}}"/>
	 * -----------------------------------------------------------
	 * author: 赵卉华 / 2015-10-10
	 * -----------------------------------------------------------
	 */
	var originalSetValue = $.fn.textbox.methods.setValue;
	$.fn.textbox.methods.setValue = function(jq, value) {
		return jq.each(function() {
			var me = $(this);
			var data = $.data(this, "textbox");
			var opts = data.options;
			var text = value;
			// datetimebox会有问题, 因为easyui已经支持formatter, 再执行一次就会报错
			if (me.is(".x-textbox")) { // 因此只支持textbox
				if (opts.formatter) {
					text = opts.formatter.call(this, value);
				} else if (opts.xextra && opts.xref.space) {
					var extras = opts.xref.space.xcall("options").extras;
					var o = { field:me.attr("textboxname") };
					if (typeof (opts.xextra) == "string") {
						o = { field:opts.xextra };
					} else if ($.isPlainObject(opts.xextra)) {
						o = $.extend({}, o, opts.xextra);
					}
					text = handleTextboxExtraValue.call(this, value, o, extras);
				}
			}

			var original = $(this).textbox("getValue");
			opts.value = "";
			me.textbox("setText", text);
			data.textbox.find(".textbox-value").val(value);
			me.val(value);
			if (original != value) {
				opts.onChange.call(this, value, original);
				me.closest("form").trigger("_change", [ this ]);
			}
		});
	};

	// value支持数组,逗号或斜杠或竖杠分隔的字符串
	// 如: Role = [ { "code": 21, "name": "活动策划" }, { "code": 22, "name": "营销推广" } ]
	// <th data-options="field:"roleCodes",xextra:{field:'Role'}>角色</th>
	// data = { roleCodes:[ 21, 22 ] }, 角色列显示为: 活动策划, 营销推广
	// data = { roleCodes:"21,22" }, 角色列显示为: 活动策划, 营销推广
	// data = { roleCodes:"21/22" }, 角色列显示为: 活动策划 / 营销推广
	// data = { roleCodes:"21|22" }, 角色列显示为: 活动策划 | 营销推广
	var handleExtraValue = function(value, o, extras) {
		var ignore = o.ignore && ( $.isArray(o.ignore) ? o.ignore : [o.ignore] );

		var temp = $.isArray(value) ? value : $.dt.isString(value) ? value.split(ARRAY_SPLIT) : [value];
		var values = [];
		$.each(temp, function(i, v) { // 过滤掉ignore的
			if (!ignore || $.inArray(v, ignore) < 0) { values.push(v); }
		});
		var separator = ", ";
		if ($.dt.isString(value)) {
			separator = value.indexOf('|') > 0 ? " | " : value.indexOf('/') > 0 ? " / " : ", ";
		}
		var items = [];
		var classes = [];
		var textes = [];
		$.each(extras[o.field].list, function(index, item) { // 保证顺序
			$.each(values, function(i, v) {
				if (String(v) != item.key) { return true; } // continue
				var cls = DEF.generateExtraClassName(o.cls, o.field, v);
				items.push( { value:v, text:item.value, cls:cls } );
				textes.push(item.value);
				if (cls && $.inArray(cls, classes) < 0) { classes.push(cls); }
			});
		});
		if (o.notFoundText) {
			$.each(values, function(i, v) { // 查找不存在于候选列表中的value
				if (v && extras[o.field].map[v] == null) {
					items.push( { value:v, text:o.notFoundText || v, notFound:true } );
					textes.push(o.notFoundText || v);
				}
			});
		}
		return { items:items, textes:textes, classes:classes, separator:separator };
	};
	var handleDatalistExtraValue = function(value, o, extras) {
		if (value == undefined || value === "" || value.length == 0) { return o.emptyText || ""; }
		var json = handleExtraValue(value, o, extras);
		// 有可能是被ignore过滤掉了, 被过滤掉了是应该返回emptyText还是空字符串, 存在逻辑冲突, 目前的选择是emptyText
		if (json.items.length == 0) { return o.emptyText || ""; }

		var list = [];
		$.each(json.items, function(index, item) {
			if (item.notFound) {
				list.push('<span title="' + item.value + '">' + item.text + '</span>');
			} else {
				list.push( item.cls ? '<span class="' + item.cls + '">' + item.text + '</span>' : item.text );
			}
		});
		return list.join(json.separator);
	};
	// 与handleDatalistExtraValue不一样的是对cls的处理
	var handleTextboxExtraValue = function(value, o, extras) {
		var me = $(this);
		var input = me.textbox("textbox");
		if (o.cls && extras[o.field]) {
			for (var key in extras[o.field].map) {
				var cls = DEF.generateExtraClassName(o.cls, o.field, key);
				cls && input.removeClass(cls);
			}
		}
		if (value == undefined || value === "" || value.length == 0) { return o.emptyText || ""; }

		var json = handleExtraValue(value, o, extras);
		// 有可能是被ignore过滤掉了, 被过滤掉了是应该返回emptyText还是空字符串, 存在逻辑冲突, 目前的选择是emptyText
		if (json.items.length == 0) { return o.emptyText || ""; }

		if (json.classes && json.classes.length == 1) {
			input.addClass(json.classes[0]); // 不支持多种不同的cls
		}
		return json.textes.join(json.separator);
	};




	/**
	 * -----------------------------------------------------------
	 * tree数据解析器, 支持将扁平pid结构转换为tree层级结构
	 * -----------------------------------------------------------
	 * datagrid数据解析器
	 * -----------------------------------------------------------
	 * 默认的配置, json支持以下两种格式
	 * { code:"SUCCESS", body:[], extra:{ total:20 } }
	 * { code:"SUCCESS", body:{ page:1, total:20, result:[] } }
	 * -----------------------------------------------------------
	 * author: 赵卉华 / 2014-03-26
	 * -----------------------------------------------------------
	 */
	// tree数据解析器
	$.fn.tree.parser = function(json, options) {
		var data = $.dt.isElement(this) && $(this).data() || {};
		var tree = data.tree || data.treegrid || data.combotree;
		var defined = {};
		var treeopts = {};
		if (tree) {
			treeopts = tree.options;
			if (treeopts.upgrade) { defined.upgrade = treeopts.upgrade; }
			if (treeopts.idField) { defined.tid = treeopts.idField; }
			if (treeopts.treeField) { defined.text = treeopts.treeField; }
			if (treeopts.parentField) { defined.parent = treeopts.parentField; }
		}
		var opts = $.extend(true, {}, $.fn.tree.parser.options, defined, options);
		var list;
		if ($.isArray(json)) { // 参数是数组, 不需要判断成功失败
			list = json;
		} else if (opts.check(json) === false) { // 失败
			list = []; // 失败返回空列表
		} else {
			// 根据配置的字段, 取数据列表
			list = $.zhh.field(json, opts.list) || [];
		}
		if (list.length == 0) {
			return [];
		} else {
			if (treeopts.prepend) {
				list.push(treeopts.prepend);
			}
			$.each(list, function() {
				// 根据指定字段设置iconCls
				// iconCls:'xxx'
				// iconMap:{field:'Gender',cls:{MALE:'blue',FEMALE:'orange'}}
				// iconMap:{field:'Gender',cls:true}
				// iconMap:{field:'Gender',cls:'{field}_{value}'}
				if (treeopts.iconCls) {
					this.iconCls = treeopts.iconCls;
				} else if (treeopts.iconMap) {
					var map = treeopts.iconMap;
					this.iconCls = DEF.generateExtraClassName(map.cls, map.field, this[map.field]);
				}
				// state 在easyui的tree中表示open/close, 因此复制到新属性$state$中
				if (this.state) {
					this.$state$ = this.state;
				}
			});
			return $.zhh.treeparse(list, opts);
		}
	};
	// datagrid数据解析器
	$.fn.datagrid.parser = function(json, options) {
		if ($.isArray(json)) { // 参数是数组, 不需要判断成功失败
			return { total:json.length, rows:json };
		}
		if (json.total && json.rows) { // easyui默认的格式, 不需要转换
			return json;
		}
		var opts = $.extend({}, $.fn.datagrid.parser.options, options);
		if (opts.check(json) === false) { // 失败
			return { total:0, rows:[] };
		} else {
			// 根据配置的字段, 取总记录数和数据列表
			var rows = $.zhh.field(json, opts.list) || [];
			var total = $.zhh.field(json, opts.total) || rows.length;
			return { total:total, rows:rows };
		}
	};
	// tree默认配置参数
	$.fn.tree.parser.options = {
		// 检查响应是否成功的函数
		check: function(json) { return DEF.isJsonSuccess(json); },
		list: "body.result || body || rows",
		copy: ["text"], // 需要复制的属性
		upgrade: true, // 未找到父节点的子节点是否升级为根节点
		tid:"id", parent:"parent || parentId", // 用于关联的ID和父ID
		text: "text || title || name"
	};
	// datagrid默认配置参数
	$.fn.datagrid.parser.options = {
		// 检查响应是否成功的函数
		check: function(json) { return DEF.isJsonSuccess(json); },
		list:"body.result || body",
		total:"body.total || extra.total"
	};

})(jQuery);





(function($) {
	/**
	 * -----------------------------------------------------------
	 * 支持复杂对象的赋值
	 * -----------------------------------------------------------
		$(form).form("load", {
			"name":"易象","address":{"city":"南京"},
			"users":[
				{"name":"汪某某","addresses":[{"city":"南京"}]},
				{"name":"赵某某","addresses":[{"city":"合肥"},{"city":"南京"}]}
			]
		});
		<form>
			<input name="name" />
			<input name="address.city" />
			<input name="users[0].name" />
			<input name="users[0].addresses[0].city" />
			<input name="users[1].name" />
			<input name="users[1].addresses[0].city" />
			<input name="users[1].addresses[1].city" />
		</form>
	 * -----------------------------------------------------------
	 * author: 赵卉华 / 2015-10-10
	 * -----------------------------------------------------------
	 */
	var originalFormLoadData = $.fn.form.methods.load;
	$.fn.form.methods.load = function(jq, data) {
		var opts = $.data(jq[0], "form").options;
		var params = opts.jsonToParams !== false ? $.zhh.jsonToParams(data) : data;
		return originalFormLoadData(jq, params);
	};






	/**
	 * -----------------------------------------------------------
	 * form.form("clear");
	 * 支持通过data-clear="false"配置字段不允许清除
	 * 支持通过data-def-value="value"配置字段的默认值
	 * -----------------------------------------------------------
	 * 如下配置, 执行form.form("clear")后, state=0, gender=0
		<form>
			<input type="hidden" data-clear="false" name="state" value="0" />
			<input type="hidden" data-def-value="0" name="gender" />
		</form>
	 * -----------------------------------------------------------
	 * author: 赵卉华 / 2016-03-20
	 * -----------------------------------------------------------
	 */
	var originalFormClear = $.fn.form.methods.clear;
	$.fn.form.methods.clear = function(jq) {
		return jq.each(function() {
			var me = $(this);
			var map = {};
			me.find("input[name][data-def-value]").each(function() {
				map[$(this).attr("name")] = $(this).attr("data-def-value");
			});
			me.find("input[textboxname][data-def-value]").each(function() {
				map[$(this).attr("textboxname")] = $(this).attr("data-def-value");
			});
			me.find("input[name][data-clear=false]").each(function() {
				if($(this).attr("type") != "radio" || $(this).prop("checked")){
					var value = $(this).val();
					if (value) { map[$(this).attr("name")] = value; }
				}
			});
			me.find("input[textboxname][data-clear=false]").each(function() {
				var value = $(this).xcall("getValue");
				if (value) { map[$(this).attr("textboxname")] = value; }
			});
			originalFormClear(me);
			me.form("load", map);
		});
	};








	/**
	 * -----------------------------------------------------------
	 * tree增加uncheckAll方法
	 * -----------------------------------------------------------
	 * $("ul.tree").tree("uncheckAll");
	 * -----------------------------------------------------------
	 * author: 赵卉华 / 2016-02-20
	 * -----------------------------------------------------------
	 */
	if (!$.fn.tree.methods.uncheckAll) {
		$.fn.tree.methods.uncheckAll = function(jq) {
			var checked = this.getChecked(jq, ["checked", "indeterminate"]);
			for (var i = 0; i < checked.length; i++) {
				this.uncheck(jq, checked[i].target);
			}
		};
	}
	$.fn.tree.methods.getAllNodes = function(jq) {
		var roots = this.getRoots(jq);
		return getChildNodes.call(this, jq, roots);
	};
	// 获取tree的子节点的递归算法
	var getChildNodes = function(jq, nodes) {
		if (!nodes || nodes.length == 0) {
			return [];
		}
		var list = [];
		for (var i=0; i<nodes.length; i++) {
			var node = nodes[i];
			list.push(node);
			if (!this.isLeaf(jq, node.target)) {
				var children = this.getChildren(jq, node.target);
				list = list.concat(getChildNodes.call(this, jq, children));
			}
		}
		return list;
	};
})(jQuery);





(function($) {
	/**
	options : {
		hash : string, // 用于判断tab唯一性和hash导航, 必填
		space: string, // 页面空间, 非必填,通过href或hash计算
		xref : string, // 指向另一个页面, 与目标页面只有hash和initial参数不同
		href : string, // 打开哪个页面, 非必填,通过hash计算
		menu : boolean, // 是否与左侧菜单关联, 默认为true
		homepage : boolean, // 首页的hash导航不同, 默认为false
	}
	*/
	var xopen = function(options) {
		var me = $(this);
		var topts = me.tabs("options");

		if (!options.hash) {
			$.log.debug("Cann't open the tab page, options.hash is null.");
			return;
		}
		var path = hashToPath((options.xref || options.hash).replace(/^#/, ""));
		if (!options.space) {
			options.space = viewToSpace(path.view);
		}
		// 如果没有view, 根据约定规则计算view
		if (!options.view) {
			options.view = path.view;
		}
		// 如果没有href, 根据约定规则计算href
		if (!options.href) {
			var pathname = location.pathname;
			var slash = pathname.lastIndexOf("/");
			var suffix = pathname.substring(Math.max(0, slash));
			var dot = suffix.lastIndexOf(".");
			options.href = path.href + (dot < 0 ? "/" : suffix.substring(dot));
		}

		// 判断TAB页签是否存在
		var all = me.tabs("tabs");
		var index = null;
		$.each(all, function() {
			var initial = this.panel("options").initial;
			if (initial && initial.hash == options.hash && initial.id == options.id) {
				index = me.tabs("getTabIndex", this);
				return false;
			}
		});

		if (index != null) {
			var curr = me.tabs("getSelected");
			if (index == me.tabs("getTabIndex", curr)) {
				// 当前TAB页签就是要打开的页签
				if (topts.refreshChildrenOnSelect) {
					curr.xreload(true); // 刷新子元素
				}
			} else {
				// 选中已经存在的TAB页签
				me.tabs("select", index);
			}
			return me;
		} else {
			// 打开新的页签
			var title = options.title;
			var text = title.length <= 10 ? title : title.substring(0, 9) + "..";
			var closable = options.closable == undefined ? true : options.closable;
			var href = options.href;
			var base = $("head meta[name=relative-path]");
			if (base.length) { href = base.attr("content") + href; }
			var extras = options.owner && options.owner.xcall("options").extras || 
				{"Boolean":{map:{"true":"是","false":"否"},list:[{key:"true",value:"是"},{key:"false",value:"否"}]}};
			return me.tabs("add", {
				cache:true, closable:closable, autoParse:false, extras:extras, loadingMessage:false,
				title:text, href:href, initial:options, tabs:me,
				refreshChildrenOnSelect:topts.refreshChildrenOnSelect,
				onBeforeLoad:function() {
					xprogress.call(this, 10);
				},
				onLoad:function() {
					xprogress.call(this, 30);
				},
				extractor:function(data) {
					// 检查用户是否登录
					var auth = $.fn.xui.defaults.checkAuthorized.call(this, data);
					if (typeof(auth) == "string") { return auth; }
					else if (auth === false) { return ""; }

					// 替换占位符
					data = $.zhh.format('{{}}', data, { initial:options });
					// 取<body>的内容
					var body = $.fn.xui.defaults.pageContentFilter;
					var matcher = body.exec(data);
					var text = matcher ? matcher[1] : data;
					// 处理<script>和<link>
					return handleScriptAndLink.call(this, text);
				},
				onBeforeDestroy:function() {
					var me = $(this);
					var options = me.panel("options");
					if (options.popups) {
						var list = [];
						for (var key in options.popups) {
							options.popups[key].dialog("destroy");
							list.push(key);
						}
						$.log.debug("dialog destroy: " + list.join(", "));
					}
				}
			});
		}
	};
	// tabs的加载进度提示
	var html = '<div class="loading"><div class="valign-middle-wrap"><div class="valign-middle-box"><div class="valign-middle"><div class="x-progressbar bootstrap striped active"></div></div></div></div></div>';
	var xprogress = function(value) {
		var panels = $(this).parent(".panel").parent(".tabs-panels"); // .parent(".tabs-container");
		if (panels.length == 0) { return; }
		var bar = panels.children(".loading").find(".x-progressbar");
		if (value == "close") { bar.closest(".loading").hide(); return; }
		if (bar.length == 0) {
			bar = $(html).appendTo(panels).find(".x-progressbar").progressbar({text:"",width:400,height:12});
		}
		bar.progressbar("setValue", value || 0).closest(".loading").show();
		if (value >= 100) {
			setTimeout(function() { bar.closest(".loading").hide(); }, 300);
		}
	};
	var handleScriptAndLink = function(text) {
		// 解决动态加载的JS在DEBUG时找不到源码的问题!
		// 解决<link>加载完成之前,JS写入的<style>内容不生效导致宽高计算出错的问题!
		var ptn = /<(script|link)[^>]*(?:src|href)\s*=\s*['"]([^'">]+)['"][^>]*\/?>(?:\s*<\/(?:script|link)>)?/gim;
		var scripts = [];
		var links = [];
		var html = [];
		var base = $("head meta[name=relative-path]").attr("content");
		for (var i = 0, matcher = null; true;)  {
			matcher = ptn.exec(text);
			if (!matcher) {
				html.push(text.substring(i));
				break;
			}
			// 取index, 文档都讲有lastIndex, 但实际测试只有index(Chrome,FF)
			// IE有lastIndex(=matcher.index+matcher[0].length), (IE8,IE11)
			// http://www.w3schools.com/jsref/jsref_obj_regexp.asp
			// http://www.w3school.com.cn/jsref/jsref_obj_regexp.asp
			// http://www.w3school.com.cn/jsref/jsref_exec_regexp.asp
			// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
			var index = matcher.index;
			if (matcher[1].toLowerCase() == "script") {
				var temp = matcher[2];
				scripts.push(base ? temp.replace(/(\.\/|\.\.\/)+/, base) : temp);
			} else {
				var temp = matcher[0];
				links.push(base ? temp.replace(/(\.\/|\.\.\/)+/, base) : temp);
			}
			html.push(text.substring(i, index));
			html.push("<!-- " + matcher[0] + " -->");
			i = index + matcher[0].length;
		}
		var pnl = $(this);
		setTimeout(function() {
			$.each(links, function() {
				pnl.append(this);
			});
			$.zhh.load(scripts);
		}, 0);
		return html.join("");
	};
	// 将hash转换为路径
	/* 作废，改用新规则
	// personnel:admin-info --> personnel/admin-info
	// user-info --> user/user-info
	// /system/personnel/admin-info/ --> system/personnel/admin-info
	var hashToPath = function(space) {
		var path = space.replace(/:/g, "/").replace(/^\/|\/$/g, "");
		if (path.indexOf("/") < 0) {
			var index = path.indexOf("-");
			if (index > 0) {
				path = path.substring(0, index) + "/" + path;
			}
		}
		return path;
	};
	var viewToSpace = function(view) { 
		return "/" + view + "/";
	};
	*/
	/*
	/* 再作废
	// 新规则，路径仅支持三或四级，前面两级路径指向首页，后面一级或二级指向子页面
	// /system/personnel/#/admin-info/ --> system/personnel/admin-info
	// 如果后面路径达到三级，则判定为绝对路径，直接返回该路径
	// /system/personnel/#/work/todo/task-info/ --> work/todo/task-info
	// 也支持静态页面的方式
	// /system/personnel/index.html#/admin-info/ --> system/personnel/admin-info
	var hashToPath = function(space) {
		var path = space.replace(/:/g, "/").replace(/^\/|\/$/g, "");
		var array = path.split("/");
		if (array.length > 2) { // 绝对路径
			return path;
		} else { // 相对路径
			// shiro登录之后有可能会带JSESSIONID  http://localhost:8888/m/;JSESSIONID=xx
			var uri = window.location.href.replace(/([;?#].*)/, "");
			var m = /\/([\-\.\w]+\/[\-\.\w]+\/)[\-\.\w]*$/.exec(uri);
			if (!m) {
				throw Error("Cann't open the tab page, path error: " + uri + "#" + space);
			}
			return m[1] + path;
		}
	};
	var viewToSpace = function(view) { 
		return "/" + view + "/";
	};
	*/
	// 新规则, 路径仅支持四级: 第一级(#号前)是模块名, 与页面的层级无关, 叫什么都可以; 后三级(#号后)定位到具体页面
	// /system/#/auth/personnel/account/ --> system/auth/personnel/account
	// 也支持静态页面的方式
	//  /system/index.html#/auth/personnel/account/ --> system/auth/personnel/account
	var hashToPath = function(space) {
		var view = space.replace(/:/g, "/").replace(/^\/|\/$/g, "");
		// shiro登录之后有可能会带JSESSIONID  http://localhost:8888/m/;JSESSIONID=xx
		var uri = window.location.href.replace(/([;?#].*)/, "");
		var m = /\/([\-\.\w]+\/)[\-\.\w]*$/.exec(uri);
		if (!m) {
			throw Error("Cann't open the tab page, path error: " + uri + "#" + space);
		}
		return { href:m[1] + view, module:m[1], view:view };
	};
	var viewToSpace = function(view) { 
		return "/" + view + "/";
	};

	$.fn.tabs.methods.xopen = function(jq, options) {
		return jq.each(function() { xopen.call(this, options); });
	};
	$.fn.tabs.methods.xclose = function(jq, options) {
		if (options == undefined) { // 没有参数, 关闭当前面板
			var pnl = jq.tabs("getSelected");
			var index = jq.tabs("getTabIndex", pnl);
			return jq.tabs("close", index);
		} else {
			return jq.tabs("close", options);
		}
	};
	$.fn.panel.methods.xopen = function(jq, options) {
		var tabs = jq.xcall("options").tabs;
		if (tabs) {
			options.owner = jq.eq(0);
			// 如果没有menu参数, 自动取当前panel的menu
			if (!options.menu && options.menu !== false) {
				var initial = jq.panel("options").initial || {};
				options.menu = initial.menu;
			}
			return xopen.call(tabs[0], options);
		} else {
			var url = options.hash;
			var params = $.param($.extend({ id:options.id }, options.initial));
			if (params) { url += (url.indexOf("?") < 0 ? "?" : "&") + params; }
			window.location.href = url;
		}
	};
	$.fn.panel.methods.xclose = function(jq, options) {
		var tabs = jq.xcall("options").tabs;
		if (tabs) {
			xprogress.call(jq[0], "close");
			var index = tabs.tabs("getTabIndex", jq);
			tabs.tabs("close", index);
			return jq;
		} else {
			return jq.panel("close", options);
		}
	};
	$.fn.panel.methods.xprogress = function(jq, options) {
		var tabs = jq.xcall("options").tabs;
		if (tabs) {
			xprogress.call(jq[0], options);
		}
		return jq;
	};
})(jQuery);


+function($) { // remote校验规则改写
	// remote支持4个参数: url, key, fields, message, ajaxOptions
	// validType:{ length:[0,50], remote:['xxx/url.json','code','id','code already exists'] }
	// validType:{ length:[0,50], remote:['xxx/url.json','code',{paramField:'fromField'},'code already exists'] }
	// validType:{ length:[0,50], remote:['xxx/url.json','code',['id',{paramField:'fromField'}],'code already exists'] }
	// validType:{ length:[0,50], remote:['xxx/url.json','code',{},'code already exists',{dataType:'json',data:{}}] }
	var originalMessage = $.fn.validatebox.defaults.rules.remote.message;
	$.fn.validatebox.defaults.rules.remote = {
        message : originalMessage,
        getBaseUrl: function(uri) {
        	return uri;
        },
        check: function(text) {
        	return text == "true";
        },
        validator : function(value, params) {
            if (!value) { return true; }
            var url = $.fn.validatebox.defaults.rules.remote.getBaseUrl.call(this, params[0]);
            var key = params[1] || "key";
            var fields = params[2];
            var message = params[3] || originalMessage;
            var ajaxData = params[4] || {};

            var data = {};
            data[key] = value;
            if (fields) {
            	var form = $(this).closest("form");
            	var json = form.serializeJson();
            	if (!$.isArray(fields)) { fields = [fields]; }
            	$.each(fields, function() {
            		if ($.dt.isString(this)) {
	            		data[this] = json[this];
            		} else {
    	            	for (var k in this) {
    	            		data[k] = json[this[k]];
    	            	}
            		}
            	});
            }
            var ajaxOptions = $.extend(true,
            	{ url:url, data:{}, dataType:"json", type:"POST", cache:false }, ajaxData, { data:data });
            ajaxOptions.async = false; // 不支持异步请求
            var text = $.ajax(ajaxOptions).responseText;
            $.fn.validatebox.defaults.rules.remote.message = message;
            return $.fn.validatebox.defaults.rules.remote.check.call(this, text);
        }
	};
}(jQuery);
